#include "PointLightRenderer.h"
#include "Singleton.h"
#include "ModelLoader.h"
#include "resource.h"

PointLightRenderer::PointLightRenderer(void)
{
	contentModule = NULL;
	pDevice = NULL;
	pEffect = NULL;
	pVertexLayout = NULL;
	sphereModel = NULL;
	pTechnique = NULL;
	pWorldVariable = NULL;
	pColorVariable = NULL;
	pIntensityVariable = NULL;
	pRadiusVariable = NULL;
	sphereModel = NULL;
}

PointLightRenderer::~PointLightRenderer(void)
{
	UnloadGraphicsContent();
}

void PointLightRenderer::UnloadGraphicsContent()
{
	pRadiusVariable = NULL;
	pIntensityVariable = NULL;
	pColorVariable = NULL;
	pWorldVariable = NULL;
	pTechnique = NULL;
	if (sphereModel.isValid()) sphereModel->Release();
	sphereModel = NULL;
	if (pVertexLayout != NULL) pVertexLayout->Release();
	pVertexLayout = NULL;
	if (pEffect != NULL) pEffect->Release();
	pEffect = NULL;
	if (contentModule != NULL) FreeLibrary(contentModule);
	contentModule = NULL;
	pDevice = NULL;
}

HRESULT PointLightRenderer::LoadGraphicsContent(EnginePointer<ModelLoader> pModelLoader, EnginePointer<ID3D10Device>& pDevice, EnginePointer<ID3D10EffectPool>& pCommonPool)
{
	HRESULT hr;

	this->pDevice = pDevice;

	contentModule = LoadLibrary(L"D3D10EngineContent.dll");
	if (contentModule == NULL) return E_FAIL;

#ifdef _DEBUG
	hr = D3DX10CreateEffectFromResource(contentModule, MAKEINTRESOURCE(POINT_RENDER_EFFECT), NULL, NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS | D3D10_SHADER_DEBUG, D3D10_EFFECT_COMPILE_CHILD_EFFECT, pDevice, pCommonPool, NULL,& pEffect, NULL, NULL);
#else
	hr = D3DX10CreateEffectFromResource(contentModule, MAKEINTRESOURCE(POINT_RENDER_EFFECT), NULL, NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, D3D10_EFFECT_COMPILE_CHILD_EFFECT, pDevice, pCommonPool, NULL,& pEffect, NULL, NULL);
#endif
	if (FAILED(hr)) return hr;

	pTechnique = pEffect->GetTechniqueByName("Render");

	D3D10_PASS_DESC PassDesc;
	pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
	hr = pDevice->CreateInputLayout(LightModelVertexLayout, LightModelVertexNumElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize,& pVertexLayout);
	if (FAILED(hr)) return hr;

	pWorldVariable = pEffect->GetVariableByName("World")->AsMatrix();
	pColorVariable = pEffect->GetVariableByName("Color")->AsVector();
	pIntensityVariable = pEffect->GetVariableByName("Intensity")->AsScalar();
	pRadiusVariable = pEffect->GetVariableByName("Radius")->AsScalar();
	pLightPositionVariable = pEffect->GetVariableByName("LightPosition")->AsVector();

	hr = pModelLoader->LoadModel(INVERTED_SPHERE_MODEL, sphereModel);
	if (FAILED(hr)) return hr;

	hr = sphereModel->LoadGraphicsContent(pDevice);
	if (FAILED(hr)) return hr;

	return S_OK;
}

HRESULT PointLightRenderer::Render(list<EnginePointer<PointLightComponent>>::iterator lightsBegin, list<EnginePointer<PointLightComponent>>::iterator lightsEnd)
{
	HRESULT hr;

	D3D10_TECHNIQUE_DESC techDesc;
	pTechnique->GetDesc(&techDesc);

	pDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	pDevice->IASetInputLayout(pVertexLayout);

	UINT offset = 0;

	for (vector<EnginePointer<ModelMesh>>::iterator it = sphereModel->meshesBegin(); it != sphereModel->meshesEnd(); it++)
	{
		pDevice->IASetVertexBuffers(0, (*it)->getNumVertexBuffers(), (*it)->getVertexBuffers(), (*it)->getStrides(),& offset);
		pDevice->IASetIndexBuffer((*it)->getIndexBuffer(), DXGI_FORMAT_R16_UINT, 0);
		for (list<EnginePointer<MeshSubset>>::iterator it2 = (*it)->subsetBegin(); it2 != (*it)->subsetEnd(); it2++)
		{
			for (UINT p = 0; p < techDesc.Passes; ++p)
			{
				for (list<EnginePointer<PointLightComponent>>::iterator it3 = lightsBegin; it3 != lightsEnd; it3++)
				{
					hr = pWorldVariable->SetMatrix((*it3)->getWorld());
					if (FAILED(hr)) return hr;
					hr = pColorVariable->SetFloatVector((*it3)->getColor());
					if (FAILED(hr)) return hr;
					hr = pIntensityVariable->SetFloat((*it3)->getIntensity());
					if (FAILED(hr)) return hr;
					hr = pRadiusVariable->SetFloat((*it3)->getRadius());
					if (FAILED(hr)) return hr;
					hr = pLightPositionVariable->SetFloatVector((*it3)->getPosition());
					if (FAILED(hr)) return hr;

					pTechnique->GetPassByIndex(p)->Apply(0);
					pDevice->DrawIndexed((UINT)(*it2)->getIndexCount(), (UINT)(*it2)->getIndexStart(), (UINT)(*it2)->getVertexStart());
				}
			}
		}
	}

	return S_OK;
}
